//==========================================================================
/**
* @file	  : Quaternion.h
* @author : cpzhang <chengpengzhang@gmail.com>
* created : 2010-5-6   11:36
* purpose : 
*/
//==========================================================================

#ifndef __Quaternion_h__
#define __Quaternion_h__

//
#include "Common.h"
#include "Basic.h"
#include "Vector3.h"
#include "Radian.h"

//
namespace Euler
{
	class _MATH_EXPORT_ Quaternion
	{
	public:
		inline Quaternion(Real x = 0.0, Real y = 0.0, Real z = 0.0, Real w = 1.0)
			:	_x(x), _y(y), _z(z), _w(w)
		{

		}
		~Quaternion(){}

		//
	public:
		inline Quaternion& operator = (const Quaternion& q)
		{
			_x = q._x;
			_y = q._y;
			_z = q._z;
			_w = q._w;

			//
			return *this;
		}

		//
		inline bool operator == (const Quaternion& q) const
		{
			return (_x == q._x) && (_y == q._y) && (_z == q._z) && (_w == q._w);
		}

		//
		inline bool operator != (const Quaternion& q) const
		{
			return !(operator==(q));
		}

		//
		inline Quaternion operator + (const Quaternion& q) const
		{
			return Quaternion(_x + q._x, _y + q._y, _z + q._z, _w + q._w);
		}

		//
		inline Quaternion operator - (const Quaternion& q) const
		{
			return Quaternion(_x - q._x, _y - q._y, _z - q._z, _w - q._w);
		}

		//
		inline Quaternion operator * (const Quaternion& q) const
		{
			return Quaternion(
					 _x * q._w + _y * q._z - _z * q._y + _w * q._x,
					-_x * q._z + _y * q._w + _z * q._x + _w * q._y,
					 _x * q._y - _y * q._x + _z * q._w + _w * q._z,
					-_x * q._x - _y * q._y - _z * q._z + _w * q._w
				);
		}

		//
		inline Quaternion operator * (const Real scale) const
		{
			return Quaternion(_x * scale, _y * scale, _z * scale, _w * scale);
		}

		//
		inline friend Quaternion operator* (Real scale, const Quaternion& q)
		{
			return q * scale;
		}
		
		//
		inline Quaternion operator - () const
		{
			return Quaternion(-_x, -_y, -_z, -_w);
		}

		// rotation of a vector by a quaternion
		inline Vector3 operator* (const Vector3& v) const
		{
			// nVidia SDK implementation
			Vector3 uv, uuv;
			Vector3 qvec(_x, _y, _z);
			uv = qvec.crossProduct(v);
			uuv = qvec.crossProduct(uv);
			uv *= (2.0f * _w);
			uuv *= 2.0f;

			return v + uv + uuv;
		}

		//
	public:
		//
		inline Quaternion conjugate() const
		{
			return Quaternion(-_x, -_y, -_z, _w);
		}

		//
		inline Real normSquare() const
		{
			return _x * _x + _y * _y + _z * _z + _w * _w;
		}

		//
		inline Real norm() const
		{
			return Basic::Sqrt(normSquare());
		}

		//
		inline Real normalize() 
		{
			Real len = norm();
			if (len > 0.0)
			{
				Real inv = 1.0f / len;
				*this = *this * inv;
			}

			//
			return len;
		}

		//
		inline Quaternion inverse() const
		{
			Real fNormSqu = normSquare();
			if (fNormSqu > 0.0)
			{
				Real fInv = 1.0f / fNormSqu;
				return conjugate() * fInv;
			}

			//
			return ZERO;
		}

		//
		inline void fromAngleAxis (const Radian& rfAngle,	const Vector3& rkAxis)
		{
			// assert:  axis[] is unit length
			//
			// The quaternion representing the rotation is
			//   q = cos(A/2)+sin(A/2)*(x*i+y*j+z*k)

			Radian fHalfAngle ( 0.5*rfAngle );
			Real fSin = Basic::Sin(fHalfAngle);

			_x = fSin*rkAxis.x;
			_y = fSin*rkAxis.y;
			_z = fSin*rkAxis.z;
			_w = Basic::Cos(fHalfAngle);
		}

		 void ToRotationMatrix (Matrix3& kRot) const;
		//
	public:
		// special values
		static const Quaternion ZERO;
		static const Quaternion IDENTITY;
		
		//
	public:
		Real	_x;
		Real	_y;
		Real	_z;
		Real	_w;
	};
}

#endif // __Quaternion_h__
